using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Timers;
using LuaInterface;

namespace ArtheaEngine
{
    public static class World
    {
        [Flag] public const int ADMINONLY = (1 << 0);

        public const int IMMORTAL = 201;
        public const int MAX_LEVEL = 210;
        public const string Name = "Arthea";
        private static readonly Timer areaUpdate = new Timer(int.MaxValue);
        private static readonly Timer charUpdate = new Timer(int.MaxValue);

        static World()
        {
            charUpdate.Elapsed += charUpdate_Elapsed;
            charUpdate.AutoReset = true;
            areaUpdate.Elapsed += areaUpdate_Elapsed;
            areaUpdate.AutoReset = true;

            DataContext = new ArtheaDataContext();
            Connections = new LinkedList<IConnection>();
            Characters = new LinkedList<Character>();
            Objects = new LinkedList<Object>();

            ScriptEngine = new Lua();

            Races = new HashSet<Race>();
            Classes = new HashSet<ClassType>();
            Abilities = new HashSet<Ability>();

            foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
            {
                if (type.IsSubclassOf(typeof (Race)) && !type.IsAbstract)
                    Races.Add((Race) Activator.CreateInstance(type));

                if (type.IsSubclassOf(typeof (ClassType)) && !type.IsAbstract)
                    Classes.Add((ClassType) Activator.CreateInstance(type));

                if (type.IsSubclassOf(typeof (Ability)) && !type.IsAbstract)
                    Abilities.Add((Ability) Activator.CreateInstance(type));
            }
        }

        public static ICollection<Ability> Abilities { get; private set; }

        public static ICollection<Race> Races { get; private set; }

        public static ICollection<ClassType> Classes { get; private set; }

        public static ArtheaDataContext DataContext { get; private set; }

        public static ICollection<IConnection> Connections { get; private set; }

        public static ICollection<Character> Characters { get; private set; }

        public static ICollection<Object> Objects { get; private set; }

        public static int Flags { get; set; }

        public static int AreaUpdatePulse { get; set; }

        public static Encoding Encoding
        {
            get { return Encoding.Default; }
        }

        public static Lua ScriptEngine { get; private set; }

        public static int CharUpdatePulse { get; set; }

        private static void areaUpdate_Elapsed(object sender, ElapsedEventArgs e)
        {
            foreach (Area a in DataContext.Areas)
            {
                a.Update();
            }
        }

        public static void Initialize()
        {
            if (DataContext.Areas.Count() == 0 || DataContext.Areas.SingleOrDefault(a => a.Name == "Limbo") == null)
            {
                var area = new Area {Name = "Limbo"};
                DataContext.Areas.InsertOnSubmit(area);
                DataContext.SubmitChanges();
                DataContext.Refresh(RefreshMode.KeepChanges, DataContext.Areas);
            }

            if (DataContext.Rooms.Count() == 0)
            {
                var room = new Room
                               {
                                   Area = DataContext.Areas.Single(a => a.Name == "Limbo"),
                                   Description = "The room is empty and clean.",
                                   Name = "A New Room"
                               };

                DataContext.Rooms.InsertOnSubmit(room);
                DataContext.SubmitChanges();
                DataContext.Refresh(RefreshMode.KeepChanges, DataContext.Rooms);
            }

            if (DataContext.Characters.OfType<NonPlayer>().Count() == 0)
            {
                var npc = new NonPlayer
                              {
                                  Area = DataContext.Areas.Single(a => a.Name == "Limbo"),
                                  Level = 1,
                                  Flags = 0,
                                  ShortDescr = "A new npc",
                                  LongDescr = "A new npc is here, looking blank.",
                                  Name = "new npc",
                                  Room = DataContext.Rooms.FirstOrDefault()
                              };
                DataContext.Characters.InsertOnSubmit(npc);
                DataContext.SubmitChanges();
                DataContext.Refresh(RefreshMode.KeepChanges, DataContext.Characters);
            }

            Update();

            charUpdate.Start();

            areaUpdate.Start();
        }

        public static void Update()
        {
            foreach (Area a in DataContext.Areas)
                a.Update();
        }

        private static void charUpdate_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (charUpdate.Interval != CharUpdatePulse)
                charUpdate.Interval = CharUpdatePulse;

            foreach (Character ch in Characters)
            {
                if (ch is Player)
                {
                    var p = ch as Player;

                    if (p.Flags.And(Player.AutoTick))
                    {
                        p.WriteLine("~?{0}~x", Util.Randomizer.Next(0, 2) == 1 ? "Tick" : "Tock");
                    }
                }
                else
                {
                    // do something
                }
            }
        }

        public static Character GetChar(string keyword)
        {
            foreach (Character ch in Characters)
            {
                var keywords = new Argument(ch.Name);

                while (!keywords.IsEmpty())
                {
                    string name = keywords.FirstArg();

                    if (name.Equals(keyword, StringComparison.CurrentCultureIgnoreCase))
                    {
                        return ch;
                    }
                }
            }
            return null;
        }
    }
}